
package ControlComponents;


import DataFormatConversionComponents.NumberConverter;
import StorageComponents.Accumulator;

/**

 * 
 * VOY A TRATRAR DE DECIFRAR COMO ELLA QUIERE QUE SETEE LOS FLAGS SEGUN DICE EL TEST FILE, Y SI ME SALE CREO
 * PODEMOS PROBAR ESE INPUT FILE DE CANTASO.

 */
public class ALU {
	
	private StatusRegister status = null;
	private int accumulatorContent[] = null;
	private Accumulator acc = null;
	private boolean lock = false;
	
	/**
	 * Initialize the ALU 
	 * @param status
	 * @param accumulator
	 */
	public ALU( StatusRegister status, Accumulator accumulator ){
		this.status = status;
		this.acc = accumulator;
	}
	
	/**
	 * Reset the accumulator value
	 */
	public void resetAccumulator( ){ 
		lock = true;
		this.accumulatorContent = acc.get(); 	
	}
	
	/**
	 * Adds the accumulator + operand + carry, and stores the result on accumulator
	 * 
	 * @param operand - 8 bit operand to be added with the accumulator
	 */
	public void addc( int operand[] ){
		boolean first = false;
		if( !lock ){ 
			this.lock = true;
			first = true;
			this.resetAccumulator();
		}
				
		boolean sign = (this.accumulatorContent[0] == 1), equal = (this.accumulatorContent[0] == operand[0]);
				
		for (int bit = 7; bit >= 0; bit--) {
			if ((operand[bit] == 0) && (accumulatorContent[bit] == 0)) {
				accumulatorContent[bit] = status.getCarry();
				status.clearCarry();
			} else if ((operand[bit] == 1) && (accumulatorContent[bit] == 1)) {
				accumulatorContent[bit] = status.getCarry();
				status.setCarry();
			} else if (status.getCarry() == 1){
				accumulatorContent[bit] = 0;
			}
			else{
				accumulatorContent[bit] = 1;
			}
		}
		
		this.acc.set(this.accumulatorContent);
		this.checkNegativeZero();
		this.checkAddcOverflow(equal, sign, (this.accumulatorContent[0] == 1) );
		if( first )
			lock = false;
	}
	
	/**
	 * Decrement the accumulator by one
	 */
	public void dec() {
		boolean first = false;
		if( !lock ){ 
			this.lock = true;
			first = true;
			this.resetAccumulator();
		}
		
		int one[] = {0, 0, 0, 0, 0, 0, 0, 1}, temp_acc[] = new int [this.accumulatorContent.length];
	    for( int pos = 0; pos < this.accumulatorContent.length; pos++ )
	    	temp_acc[pos] = this.accumulatorContent[pos];
		
		acc.set(one);
		this.neg();
 
		this.addc(temp_acc);
		
		if( first )
			lock = false;
	}
	
	/**
	 * Negates the value on the accumulator
	 * 
	 */
	public void neg( ){
		boolean first = false;
		if( !lock ){ 
			this.lock = true;
			first = true;
			this.resetAccumulator();
		}
		
		if (new NumberConverter().binaryToDecimal(accumulatorContent) != 0) {
			for (int bit = 0; bit < 8; bit++)
				this.accumulatorContent[bit] = (this.accumulatorContent[bit] + 1) % 2;

			int one[] = { 0, 0, 0, 0, 0, 0, 0, 1 };
			this.status.clearCarry();
			this.addc(one);
		}

		if( first )
			lock = false;
	}
	
	/**
	 * Stores the result of an OR operation on the operand and accumulator
	 * 
	 * @param operand - 8 bit operand to be "ORed" with the accumulator
	 */
	public void or( int operand[] ){	
		boolean first = false;
		if( !lock ){ 
			this.lock = true;
			first = true;
			this.resetAccumulator();
		}
		
		for (int bit = 7; bit >= 0; bit--)
			if ((operand[bit] == 0) && (accumulatorContent[bit] == 0))
				accumulatorContent[bit] = 0;
			else
				accumulatorContent[bit] = 1;			
		
		this.checkNegativeZero();
		this.acc.set(accumulatorContent);
		
		if( first )
			lock = false;
	}
	
	/**
	 * Stores the result of an AND operation on the operand and accumulator
	 * 
	 * @param operand - 8 bit operand to be "ANDed" with the accumulator
	 */
	public void and( int operand[] ){
		boolean first = false;
		if( !lock ){ 
			this.lock = true;
			first = true;
			this.resetAccumulator();
		}
		
		for (int bit = 7; bit >= 0; bit--)
			if ((operand[bit] == 1) && (accumulatorContent[bit] == 1))
				accumulatorContent[bit] = 1;
			else
				accumulatorContent[bit] = 0;			
		
		this.checkNegativeZero();
		this.acc.set(accumulatorContent);

		if( first )
			lock = false;
	}
	
	/**
	 * Stores the result of an XOR operation on the operand and accumulator
	 * 
	 * @param operand - 8 bit operand to be "XORer" with the accumulator
	 */
	public void xor( int operand[] ){
		boolean first = false;
		if( !lock ){ 
			this.lock = true;
			first = true;
			this.resetAccumulator();
		}
		
		for( int bit = 0; bit < 8; bit++ )
			this.accumulatorContent[bit] ^= operand[bit]; 
		
		this.checkNegativeZero();
		this.acc.set(accumulatorContent);
		
		if( first )
			lock = false;
	}

	/**
	 * Rotates to the right by one the accumulator using the carry
	 */
	public void rlc() {
        int temp_acc[] = acc.get(), temp_carry = this.status.getCarry();
		
		if( temp_acc[0] == 1 )
			this.status.setCarry();
		else
			this.status.clearCarry();
		
		for( int pos = 0; pos < temp_acc.length - 1; pos++ )
			temp_acc[ pos ] = temp_acc[ pos + 1 ];
		
		temp_acc[ temp_acc.length - 1 ] = temp_carry;		
		
		this.accumulatorContent = temp_acc;
		this.acc.set(accumulatorContent);
		this.checkNegativeZero();
	}

	/**
	 * Rotates to the right by one the accumulator using the carry
	 */
	public void rrc() {
        int temp_acc[] = acc.get(), temp_carry = this.status.getCarry();
		
		if( temp_acc[temp_acc.length - 1] == 1 )
			this.status.setCarry();
		else
			this.status.clearCarry();
		
		for( int pos = temp_acc.length - 1; pos > 0; pos-- )
			temp_acc[ pos ] = temp_acc[ pos - 1 ];

		temp_acc[ 0 ] = temp_carry;		
		
		this.accumulatorContent = temp_acc;		
		this.acc.set(accumulatorContent);		
		this.checkNegativeZero();
	}

	/**
	 * Multiply the first four bit of the operand and accumulator
	 * @param operand - the operand to multiply
	 */
	public void mul(int operand[]) {
		boolean first = false;
		if( !lock ){ 
			this.lock = true;
			first = true;
			this.resetAccumulator();
		}		
		
		boolean sign = (this.accumulatorContent[0] == 1), equal = (this.accumulatorContent[0] == operand[0]);
		
		operand = this.bitFormat(operand, operand[0]);
		this.accumulatorContent = this.bitFormat(accumulatorContent, this.accumulatorContent[0]);
		
		if( sign )
			this.neg();
		
		int by = new NumberConverter().binaryToDecimal(this.accumulatorContent);
		
		int clear[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
		this.accumulatorContent = clear;
				
		for( int rep = 0; rep < by; rep++ ){	
			status.clearCarry();
			this.addc( operand );
		}	
				
		if( sign )
			this.neg();
		
		this.checkNegativeZero();		
		this.checkMulOverflow(equal, sign, (this.accumulatorContent[0] == 1) );
		this.acc.set(accumulatorContent);
		
		if( first )
			lock = false;
	}
	
	
	/**
	 * Divide the first four bit of the operand and accumulator
	 * @param operand - the operand to divide
	 */
	public void div(int operand[]) {
		boolean first = false;
		if( !lock ){ 
			this.lock = true;
			first = true;
			this.resetAccumulator();
		}		
				
		boolean sign = (this.accumulatorContent[0] == 1), equal = (this.accumulatorContent[0] == operand[0]);
		
		operand = this.bitFormat(operand, operand[0]);
		this.accumulatorContent = this.bitFormat(accumulatorContent, this.accumulatorContent[0]);
		
		this.printArray(operand, "OPERAND");
		this.printArray(accumulatorContent, "ACC");
		
		if( operand[0] == 0  ){
			int temp[] = new int[8]; 
			temp = this.accumulatorContent.clone();
			
			this.accumulatorContent = operand.clone();	
			this.neg();
			
			operand = this.accumulatorContent.clone();
			this.accumulatorContent = temp.clone();
		}
		
		if( sign )
			this.neg();
		
		this.printArray(operand, "OPERAND");
		this.printArray(accumulatorContent, "ACC");
		
		int counter = 0;
		while( new NumberConverter().binaryToDecimal(accumulatorContent) < 128 ){	
			this.status.clearCarry();
			this.addc( operand );
			counter++;
		}			
		int binary[] = new NumberConverter().integerToBinary(counter - 1);
		int result[] = new int[8];
		
		for( int pos = 0; pos < binary.length; pos++ )
			result[8 + pos - binary.length] = binary[pos];
		
		this.accumulatorContent = result.clone();
		if( !equal )
			this.neg( );
		this.acc.set( this.accumulatorContent );
		
		this.checkNegativeZero();		
		this.checkMulOverflow(equal, sign, (this.accumulatorContent[0] == 1) );

		if( first )
			lock = false;
	}
	
	/**
	 * Format the bit of the value to four
	 * @param operand - the value to format
	 * @param sign - the sign of the value
	 * @return the formatted value
	 */
	public int[] bitFormat( int operand[], int sign ){
		for( int bit = 0; bit < 4; bit++ )
			operand[bit] = sign;
		
		return operand;
	}
	
	/**
	 * Check if an overflow occurred
	 * @param equal - if equal
	 * @param sign - if negative
	 * @param newSign -  if result is negative
	 */
	private void checkMulOverflow(boolean equal, boolean sign, boolean newSign) {
		if( equal && newSign )
			status.setOverflow();
		else 
			if( !equal && !newSign )
				status.setOverflow();
			else
				status.clearOverflow();
	}
	
	/**
	 * Check if an overflow occurred
	 * @param equal - if equal
	 * @param sign - if negative
	 * @param newSign -  if result is negative
	 */
	private void checkAddcOverflow( boolean equal, boolean sign, boolean newSign ){
		if( equal && (sign != newSign) )
			status.setOverflow();
		else
			status.clearOverflow();
	}
	
	/**
	 * Check if negative and zero flags should be set
	 */
	private void checkNegativeZero(){
		if( accumulatorContent[0] == 1 )
			status.setNegative();
		else
			status.clearNegative();
	
		//zero must always be positive for this to work
		int a = new NumberConverter().binaryToDecimal(accumulatorContent);
		if(  a == 0 )
			status.setZero();
		else
			status.clearZero();			
	}
	
	/**
	 * For testing purposes
	 * @param array -the array to show
	 */
	public void printArray( int array[], String name ){
		System.out.print(name + " ");
		for( int bit = 0; bit < array.length; bit++ )
			System.out.print( array[bit] );
	}
}
